當我們將專案中的程式碼依照各自的的功能分開成不同的檔案時,常常會遇到在不同的原始碼間共用變數的需求,這個時候C/C++提供了一個方便的關鍵字:extern,但是剛開始其實不太懂這個關鍵字背後的原理是什麼,只知道如果要共用一個變數就加 extern,直到後來出現了bug後,才了解到宣告與定義之間的區別。
接下來會介紹宣告與定義的原理,並有一個多檔案共用變數的範例。
C++的編譯方式為個別編譯(separate compilation),能讓程式分為數個檔案,並且每一個都能獨立編譯成 xxx.o檔,最後才練接(linking)成最後的執行檔。
當一個程式被分為多個檔案,我們就需要一種在檔案間共用程式碼的方法,就是先宣告(declarations),再去其他地方尋找定義(definitions),通常在.h檔中宣告,在.cpp中定義。
通常宣告一個物件的同時也會被定義,除非符合以下規則
接下來多檔案共用變數的範例就是其中的第二點例外, 先使用修飾符號extern宣告後,在鍊結階段去其他檔案尋找定義。
當需要多檔案共用變數時,則必須使用 extern ,這個關鍵字用於將變數聲明為全域變數,以便可以從程序中的任何位置訪問在同一文件或另一個文件的另一個作用域中聲明的變數。當連結器在全域變數宣告之前看到 extern 時,它會在其他文件中尋找定義。
$ git clone https://github.com/m11112089/2023_iT_CMake.git
$ cd ~/2023_iT_CMake/Day7
在count.h第6行使用extern修飾"宣告", 在count.cpp第2行才會"定義"
include/count.h
extern int counter;
src/count.cpp
int counter = 0;
$ cmake .
$ make
$ ./main
kai@esoc:~/2023_iT_CMake/Day7/build$ ./main
0
0
1
1
2
2
可以看到 counter 這個變數在 main.cpp 和 count.cpp 中共用同一個數值。
但是使用extern全域變數必須小心一點,可以在不同作用域中使用相同的變數名稱定義出不同的物件,可以理解為區域變數和全域變數之間的區別。
將main.cpp的第6行解除註解之後,結果則完全不同。
src/main.cpp
int counter = 10;//並不會修改到count.cpp中定義的counter
kai@esoc:~/2023_iT_CMake/Day7/build$ ./main
0
10
1
10
1
11
原本預期的output是10, 10, 11, 11, 12, 12但是因為第6行定義了在main.cpp的main function作用域中新的的counter,因此結果會是0, 10, 1, 10, 1, 11
C 語言的宣告、定義、儲存類型 (storage class) 與連結性 (linkage)
書籍: C++ Primer 2.2.2變數宣告與定義
書籍: 大規模c++程式設計 1.1.1宣告與定義
https://learn.microsoft.com/zh-tw/cpp/cpp/extern-cpp?view=msvc-170